import abc
from coin.support.telemetry.latency_recorder import LifeOfSignal, use_latency_recorder


class PassiveExecutorInterface(metaclass=abc.ABCMeta):
  def submit_mm_orders(self,
                       *,
                       sell_price,
                       buy_price,
                       sell_qty=None,
                       buy_qty=None,
                       sell_price_pull=None,
                       buy_price_pull=None,
                       sell_price_push=None,
                       buy_price_push=None,
                       cancel_all_existing_sell=False,
                       cancel_all_existing_buy=False):
    # It merely checks the obvious validity of the input. Do not
    # adjust inputs in the interface level.

    if sell_price is not None:
      assert sell_price > 0
    if buy_price is not None:
      assert buy_price > 0
    if sell_price is not None and buy_price is not None:
      assert sell_price > buy_price, (sell_price, buy_price)

    if sell_qty is not None:
      assert sell_qty > 0
    if buy_qty is not None:
      assert buy_qty > 0
    # if sell_qty is not None and buy_qty is not None:
    # allow up to 3x
    # assert abs(sell_qty - buy_qty) / (sell_qty + buy_qty) < 0.5

    if sell_price is not None and sell_price_pull is not None:
      assert sell_price > sell_price_pull, (sell_price, sell_price_pull)
    if buy_price is not None and buy_price_pull is not None:
      assert buy_price < buy_price_pull, (buy_price, buy_price_pull)
    if sell_price is not None and sell_price_push is not None:
      assert sell_price < sell_price_push, (sell_price, sell_price_push)
    if buy_price is not None and buy_price_push is not None:
      assert buy_price > buy_price_push, (buy_price, buy_price_push)

    ret = self.submit_mm_orders_impl(sell_price=sell_price,
                                     buy_price=buy_price,
                                     sell_qty=sell_qty,
                                     buy_qty=buy_qty,
                                     sell_price_pull=sell_price_pull,
                                     buy_price_pull=buy_price_pull,
                                     sell_price_push=sell_price_push,
                                     buy_price_push=buy_price_push,
                                     cancel_all_existing_sell=cancel_all_existing_sell,
                                     cancel_all_existing_buy=cancel_all_existing_buy)
    return ret

  @abc.abstractmethod
  def submit_mm_orders_impl(self):
    raise NotImplementedError()

  def manage2_mm_orders(self):
    return self.manage2_mm_orders_impl()

  @abc.abstractmethod
  def manage2_mm_orders_impl(self):
    raise NotImplementedError()

  def manage_mm_orders(self,
                       sell_price,
                       buy_price,
                       *,
                       sell_qty=None,
                       buy_qty=None,
                       sell_price_pull=None,
                       buy_price_pull=None,
                       sell_price_push=None,
                       buy_price_push=None,
                       cancel_all_existing_sell=False,
                       cancel_all_existing_buy=False):
    with use_latency_recorder(self._latency_recorder,
                              LifeOfSignal.BEFORE_MANAGE_MM_ORDERS,
                              LifeOfSignal.AFTER_MANAGE_MM_ORDERS):
      self.submit_mm_orders(sell_price=sell_price,
                            buy_price=buy_price,
                            sell_qty=sell_qty,
                            buy_qty=buy_qty,
                            sell_price_pull=sell_price_pull,
                            buy_price_pull=buy_price_pull,
                            sell_price_push=sell_price_push,
                            buy_price_push=buy_price_push,
                            cancel_all_existing_sell=cancel_all_existing_sell,
                            cancel_all_existing_buy=cancel_all_existing_buy)
      self.manage2_mm_orders()
